

/**
 * @file
 * List_t implementation.
 */

#include <stdlib.h>
#include <assert.h>

#include "List_t.f.h"

/* ************************************************************************** */
/* Constructor and destructor.                                                */
/* ************************************************************************** */

List_t * list_new( void ( * elementDestructor)( void * ) )
{
    List_t * ret = (List_t *) malloc( sizeof( List_t ) );
    if ( ret == NULL ) return NULL;
    if ( ! list_construct( ret, elementDestructor ) )
    {
        /* Constructor failed. */
        free( ret );
        return NULL;
    }

    return ret;
}


List_t * list_newCopy( List_t * l, void * ( * clone)( void * ) )
{
    List_t * ret = (List_t *) malloc( sizeof( List_t ) );
    if ( ret == NULL ) return NULL;
    if ( ! list_constructCopy( ret, l, clone ) )
    {
        /* Constructor failed. */
        free( ret );
        return NULL;
    }

    return ret;
}


void list_delete( List_t * thisList )
{
    list_destroy( thisList );
    free( thisList );
}

/* ************************************************************************** */
/* Queries.                                                                   */
/* ************************************************************************** */

int list_isEmpty( List_t * thisList )
{
    assert( thisList != NULL );
    return thisList->head == NULL;
}


unsigned long int list_getSize( List_t * thisList )
{
    assert( thisList != NULL );
    /* @TODO iterating over elements to get the length,
       or maintaining a counter inside the list?
       - Iteration: slow, but less memory. Users could maintain a counter by itself.
       - Counter: fast, but more memory. Some SPECIAL operations could became slower.
    */
}



/* ************************************************************************** */
/* Interface methods.                                                         */
/* ************************************************************************** */


void * list_addTail( List_t * thisList, void * element )
{
    ListLink_t * newLink;

    assert( thisList != NULL );


    newLink = (ListLink_t *) malloc( sizeof( ListLink_t ));
    if (newLink == NULL ) return NULL;

    if ( thisList->tail != NULL )
    {
        thisList->tail->next = newLink;
        newLink->prev = thisList->tail;
    }
    else
    {
        thisList->head = newLink;
        newLink->prev = NULL;
    }

    newLink->next = NULL;
    newLink->element = element;
    thisList->tail = newLink;

    return element;
}


void * list_getHead( List_t * thisList )
{
    assert( thisList != NULL );
    if ( thisList->head == NULL ) return NULL;
    return thisList->head->element;
}


void list_removeHead( List_t * thisList )
{
    ListLink_t * l;
    assert( thisList != NULL );

    if ( thisList->head == NULL ) return;

    l = thisList->head;
    thisList->head = l->next;
    if ( l->next != NULL ) l->next->prev = NULL;
    free( l );
}


void list_removeAndDeleteHead( List_t * thisList )
{
    /* @TODO Implement this method. */
}


/* ************************************************************************** */
/* List init and destroy methods.                                             */
/* ************************************************************************** */


int list_construct( List_t * thisList, void ( * elementDestructor)( void * ) )
{
    assert( thisList != NULL );

    thisList->elementDestructor = elementDestructor;
    thisList->head = NULL;
    thisList->tail = NULL;

    /* In this simple example, constructor never fails. */
    return 1;
}


int list_constructCopy( List_t * thisList, List_t * list, void * ( * clone)( void * ) )
{
    ListLink_t * l;
    ListLink_t * newLink;

    assert( thisList != NULL );
    assert( list != NULL );

    thisList->elementDestructor = list->elementDestructor;
    thisList->head = NULL;
    thisList->tail = NULL;

    if ( clone != NULL )
    {
        for ( l = list->head; l != NULL; l = l->next )
        {
            /* In this example, supposing that malloc & clone never fail: */
            newLink = (ListLink_t *) malloc( sizeof(ListLink_t));
            newLink->element = clone( l->element );
            /* Adding newLink as tail: */
            newLink->prev = thisList->tail;
            newLink->next = NULL;
            thisList->tail = newLink;
            /* If newLink is the first element, set it also as head: */
            if ( thisList->head == NULL ) thisList->head = newLink;
        }
    }
    else
    {
        for ( l = list->head; l != NULL; l = l->next )
        {
            /* In this example, supposing that malloc & clone never fail: */
            newLink = (ListLink_t *) malloc( sizeof(ListLink_t));
            /* Aliasing instead of cloning: */
            newLink->element = l->element;
            /* Adding newLink as tail: */
            newLink->prev = thisList->tail;
            newLink->next = NULL;
            thisList->tail = newLink;
            /* If newLink is the first element, set it also as head: */
            if ( thisList->head == NULL ) thisList->head = newLink;
        }
    }

    /* Returning success: */
    return 1;
}


void list_destroy( List_t * thisList )
{
    register ListLink_t * i;
    register ListLink_t * next = NULL;

    assert( thisList != NULL );

    /* "If" moved outside the "for" to optimize performances. */
    if ( thisList->elementDestructor != NULL )
    {
        /* The list has the ownership of stored elements. */
        for ( i = thisList->head; i != NULL; i = next )
        {
            thisList->elementDestructor( i->element );
            /* Updating "i" in this way, to avoid accesses
               to invalid memory areas */
            next = i->next;
            free( i );
        }
    }
    else
    {
        /* The list has NOT the ownership of stored elements. */
        for ( i = thisList->head; i != NULL; i = next )
        {
            next = i->next;
            free( i );
        }
    }
}
